/*
 * @Author       : Li Ming Yang
 * @Date         : 2023-03-20 10:45:04
 * @Description  : 常用方法
 * @LastEditTime : 2024-06-12 11:42:07
 */
import store from "@/store"
import manifest from "@/manifest.json"
import { isString, isEmpty } from "./check"

/**
 * @description 消息提示框
 * @param {String} title 提示内容
 * @param {String} icon 可选值 success loading 默认 none
 * @param {Number} duration 多少毫秒后隐藏
 */
export function toast(data) {
  const routes = getCurrentPages()
  const route = routes[routes.length - 1].route
  setTimeout(() => {
    if (isString(data)) {
      uni.$emit(`${route}-toast`, { content: data })
    } else {
      uni.$emit(`${route}-toast`, data)
    }
  }, 50)
}

/**
 * @description 加载
 * @param {String|Object} args 参数
 */
export function loading(...args) {
  const routes = getCurrentPages()
  const route = routes[routes.length - 1].route
  uni.$emit(`${route}-loading`, ...args)
}

/**
 * @description 消息弹窗
 * @param String content 提示内容
 * @param String title 提示标题
 * @param Function callback 回调方法
 */
export function alert(title = "提示", content = "", callback) {
  callback = callback || function () {}
  uni.showModal({
    content,
    title,
    confirmColor: store.getters.themeColor,
    showCancel: false,
    success: (res) => {
      if (res.confirm) {
        callback()
      }
    }
  })
}

/**
 * @description 消息弹窗
 * @param {object} options 配置
 */
export function dialog(options = {}) {
  const routes = getCurrentPages()
  const route = routes[routes.length - 1].route
  if (isString(options)) {
    uni.$emit(`${route}-dialog`, { title: "提示", content: options })
  } else {
    uni.$emit(`${route}-dialog`, options)
  }
}

/**
 * @description 消息确认弹窗
 * @param {object} options 配置
 */
export function confirm(options = {}) {
  return new Promise((resolve, reject) => {
    uni.showModal({
      title: "温馨提示",
      confirmColor: store.getters.themeColor,
      ...options,
      success: (res) => {
        if (res.confirm) {
          resolve()
        } else if (res.cancel) {
          reject("确认弹窗取消")
        }
      },
      fail: (err) => {
        reject(err)
      }
    })
  })
}

/**
 * @description 页面跳转 参数以对象传递
 */
export function toView(params) {
  // 没传参不执行相应操作
  if (isEmpty(params)) return
  // 判断是否登录
  const login = store.getters.login
  const mobile = store.getters.mobile
  // 如果需要验证但是未登录就拦截并提示登录
  if ((params.login && !login) || (params.mobile && !mobile)) {
    const routes = getCurrentPages()
    const route = routes[routes.length - 1].route
    uni.$emit(`${route}-login`)
  } else {
    setTimeout(() => this.$u.route(params), 50)
  }
}

/**
 * 延时指定的时间后执行回调函数
 * @param {number} delay - 延时时间（毫秒）
 * @returns {Promise} - 返回一个Promise对象
 */
export function delay(delay) {
  return new Promise((resolve) => {
    setTimeout(resolve, delay)
  })
}

/**
 * 获取web端环境
 * @returns {String} - 环境
 */
export function getWebEnv() {
  let env = "web"
  // #ifdef WEB
  const ua = navigator.userAgent.toLowerCase()
  if (ua.match(/MicroMessenger/i)) env = "wxWeb"
  if (ua.match(/MicroMessenger/i) && ua.match(/miniprogram/i)) env = "wxMp"
  // #endif
  return env
}

/**
 * 日志打印
 */
export function log(type, content) {
  console.log(
    `%c ${type} %c ${content} `,
    "padding: 2px 1px; border-radius: 3px 0 0 3px; color: #fff; background: #606060; font-weight: bold;",
    "padding: 2px 1px; border-radius: 0 3px 3px 0; color: #fff; background: #42c02e; font-weight: bold;"
  )
}

/**
 * 获取web端业务环境
 * @returns {String} - 环境
 */
export function getWebEvn() {
  const ua = navigator.userAgent.toLowerCase()
  const href = window.location.href.toLowerCase()
  const evns = {
    mp: "mp",
    ali: "ali",
    boc: "boc",
    pab: "pab",
    cib: "cib",
    icbc: "icbc|icbctime",
    ysf: "ysf|ysftime",
    efz: "efz|efztime",
    abc: "abc|abctime",
    yzf: "yzf",
    gat: "gat",
    sft: "sft|sfttime",
    cmbc: "cmbc|cmbccard",
    ulejf: "ulejf",
    test: "test",
    webview: "webview",
    ibl: "ibl",
    web: "web|h5"
  }
  let evn = "web"
  if (ua.match(/MicroMessenger/i)) evn = "wxWeb"
  Object.entries(evns).forEach(([name, rule]) => {
    let str = new RegExp(`evn=(${rule})(?=&|$)`)
    if (str.test(href)) evn = name
  })
  return evn
}

/**
 * 从app跳转到微信小程序
 * @param {number} delay - 延时时间（毫秒）
 * @returns {Promise} - 返回一个Promise对象
 */
export function launchMiniProgram(params = {}) {
  // #ifdef APP-PLUS
  // id 小程序原始ID（以gh开头）
  // type 环境（默认0）0-正式版，1-开发版，2-体验版
  // path 拉起小程序页面的可带参路径，如：传入 "?foo=bar"
  plus.share.getServices((res) => {
    let sweixin = null
    for (let i in res) {
      if (res[i].id == "weixin") {
        sweixin = res[i]
      }
    }
    if (sweixin) {
      sweixin.launchMiniProgram(params)
    }
  })
  // #endif
}

// #ifdef H5
/**
 * 判断是否为钉钉内部浏览器
 * @returns {Boolean} - 是否
 */
export function isDingTalkBrowser() {
  const ua = window.navigator.userAgent
  return ua.includes("DingTalk")
}

export function setWebIcon(url) {
  let favicon = document.querySelector('link[rel="icon"]')
  if (favicon !== null) {
    favicon.href = url
  } else {
    favicon = document.createElement("link")
    favicon.rel = "icon"
    favicon.href = url
    document.head.appendChild(favicon)
  }
}

/**
 * 加载脚本的方法，返回一个Promise对象。
 * @param {string} url - 要加载的脚本的URL。
 * @returns {Promise} - 表示脚本加载状态的Promise对象。
 */
export function loadScript(url, force = false) {
  return new Promise((resolve, reject) => {
    // 检查是否已经加载过相同的脚本
    if (!force) {
      const scripts = document.getElementsByTagName("script")
      for (let i = 0; i < scripts.length; i++) {
        if (scripts[i].src === url && scripts[i].loaded) {
          resolve() // 已加载过，直接解析Promise
          return
        }
      }
    }

    // 创建新的脚本元素
    const script = document.createElement("script")
    script.src = url

    script.onload = () => {
      script.loaded = true
      resolve()
    }

    script.onerror = () => {
      reject("加载脚本失败: " + url)
    }

    document.head.appendChild(script)
  })
}

/**
 * @description: 表单提交
 * @param {*} action 表单地址
 * @param {*} method 提交方式
 * @param {*} param 参数
 * @return {*}
 */
export const formSubmit = (action, method, param = {}) => {
  // 创建一个隐藏的表单
  const form = document.createElement("form")
  form.style.display = "none"
  form.method = method.toUpperCase()
  form.action = action

  // 遍历param对象，创建表单元素
  Object.entries(param).forEach(([key, value]) => {
    const input = document.createElement("input")
    input.type = "hidden"
    input.name = key
    input.value = value
    form.appendChild(input)
  })

  // 将表单添加到body中
  document.body.appendChild(form)
  // 提交表单
  setTimeout(() => {
    form.submit()

    setTimeout(() => {
      document.body.removeChild(form)
    }, 300)
  }, 700)
  // 从body中移除表单
}
// #endif

/**
 * @description 获取url中的参数
 * @return 参数对象
 */
export function getUrlParams(urlParams = "") {
  let params = {}
  // #ifdef H5
  decodeURIComponent(urlParams || window?.location?.href).replace(/([^?&=]+)=([^&]+)/g, (_, k, v) => (params[k] = v))
  // #endif
  // #ifndef H5
  try {
    urlParams = urlParams.replace("?", "") // 去掉可能存在的问号
    const paramPairs = urlParams.split("&")
    for (const pair of paramPairs) {
      const [key, value] = pair.split("=")
      params[key] = value
    }
  } catch (error) {
    params = {}
  }

  // #endif
  return params
}

/**
 * @description 转换对象为url参数
 * @return url参数字符串
 */
export function encodeParams(params = {}, isPrefix = true) {
  let prefix = isPrefix ? "?" : ""
  let _result = []
  for (let key in params) {
    let value = params[key]
    // 去掉为空的参数
    if (["", undefined, null].includes(value)) {
      continue
    }
    if (value.constructor === Array) {
      value.forEach((_value) => {
        _result.push(encodeURIComponent(key) + "[]=" + encodeURIComponent(_value))
      })
    } else {
      _result.push(encodeURIComponent(key) + "=" + encodeURIComponent(value))
    }
  }
  return _result.length ? prefix + _result.join("&") : ""
}

/**
 * @description uuid
 * @return {String}
 */
export function uuid() {
  let d = new Date().getTime()
  const uuid = "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function (c) {
    const r = (d + Math.random() * 16) % 16 | 0
    d = Math.floor(d / 16)
    return (c === "x" ? r : (r & 0x3) | 0x8).toString(16)
  })
  return uuid
}

/**
 * 数组对象去重
 * @param {Array} arr - 输入数组
 * @param {String} key - 对象key
 * @returns {Array} - 去重后数组
 */
export function uniqueArrayObjects(arr = [], key = "") {
  const res = new Map()
  return arr.filter((item) => !res.has(item[key]) && res.set(item[key], 1))
}

/**
 * 将数字向下取整并保留n位小数
 * @param {Number} num - 需要处理的数字
 * @param {Number} decimalPlaces - 需要保留的小数位数
 * @return {Number} - 处理后的数字
 */
export function floorToDecimalPlaces(num, decimalPlaces = 2) {
  // 计算10的n次方，用于将小数点向右移动n位
  var multiplier = Math.pow(10, decimalPlaces)
  // 将小数点向右移动n位，然后向下取整，最后再将小数点移回原来的位置
  return Math.floor(+parseFloat(Number(num * multiplier).toPrecision(15))) / multiplier
}

/**
 * @description 对象深度克隆
 * @param {Object} obj 克隆对象
 * @return {Object}
 */
export function clone(obj) {
  // 对常见的“非”值，直接返回原来值
  if ([null, undefined, NaN, false].includes(obj)) return obj
  if (typeof obj !== "object" && typeof obj !== "function") {
    //原始类型直接返回
    return obj
  }
  var o = Array.isArray(obj) ? [] : {}
  for (let i in obj) {
    // eslint-disable-next-line no-prototype-builtins
    if (obj.hasOwnProperty(i)) {
      o[i] = typeof obj[i] === "object" ? clone(obj[i]) : obj[i]
    }
  }
  return o
}

/**
 * @description 对象深度合并
 * @param {Object} target 目标对象
 * @param {Object} source 源对象
 * @return {Object}
 */
export function merge(target = {}, source = {}) {
  target = clone(target)
  if (typeof target !== "object" || typeof source !== "object") return false
  for (var prop in source) {
    // eslint-disable-next-line no-prototype-builtins
    if (!source.hasOwnProperty(prop)) continue
    if (prop in target) {
      if (typeof target[prop] !== "object") {
        target[prop] = source[prop]
      } else {
        if (typeof source[prop] !== "object") {
          target[prop] = source[prop]
        } else {
          if (target[prop]?.concat && source[prop]?.concat) {
            target[prop] = target[prop].concat(source[prop])
          } else {
            target[prop] = merge(target[prop], source[prop])
          }
        }
      }
    } else {
      target[prop] = source[prop]
    }
  }
  return target
}

/**
 * @description 函数防抖 短时间内多次触发同一事件，只执行最后一次，或者只执行最开始的一次，中间的不执行
 * @param Function func 目标函数
 * @param Number wait 延迟执行毫秒数
 * @param Booleans immediate true - 立即执行， false - 延迟执行
 */
let timeout = null
export function debounce(func, wait = 500, immediate = false) {
  // 清除定时器
  if (timeout !== null) clearTimeout(timeout)
  // 立即执行，此类情况一般用不到
  if (immediate) {
    var callNow = !timeout
    timeout = setTimeout(function () {
      timeout = null
    }, wait)
    if (callNow) typeof func === "function" && func()
  } else {
    // 设置定时器，当最后一次操作后，timeout不会再被清除，所以在延时wait毫秒后执行func回调方法
    timeout = setTimeout(function () {
      typeof func === "function" && func()
    }, wait)
  }
}

/**
 * @description 函数节流 连续触发事件但是在 n 秒中只执行一次函数。即 2n 秒内执行 2 次
 * @param Function func 函数
 * @param Number wait 延迟执行毫秒数
 * @param Booleans immediate true - 立即执行， false - 延迟执行
 */
// eslint-disable-next-line no-unused-vars
let timer, flag
export function throttling(func, wait = 500, immediate = true) {
  if (immediate) {
    if (!flag) {
      flag = true
      // 如果是立即执行，则在wait毫秒内开始时执行
      typeof func === "function" && func()
      timer = setTimeout(() => {
        flag = false
      }, wait)
    }
  } else {
    if (!flag) {
      flag = true
      // 如果是非立即执行，则在wait毫秒内的结束处执行
      timer = setTimeout(() => {
        flag = false
        typeof func === "function" && func()
      }, wait)
    }
  }
}

/**
 * @description 获取设备信息
 */
export function getDeviceInfo(callback = () => {}) {
  let device = {}
  // #ifdef APP-PLUS
  uni.getSystemInfoSync({
    success: (systemInfo) => {
      Object.assign(device, systemInfo)
      plus.runtime.getProperty(plus.runtime.appid, (widgetInfo) => {
        Object.assign(device, widgetInfo)
        plus.device.getInfo({
          success: (info) => {
            Object.assign(device, info)
            device.appid = manifest.appid
            callback(device)
          }
        })
      })
    }
  })
  // #endif
  // #ifndef APP-PLUS
  uni.getSystemInfoSync({
    success: (systemInfo) => {
      Object.assign(device, systemInfo, manifest)
      device.appid = manifest.appid
      callback(device)
    }
  })
  // #endif
}

// #ifdef MP-WEIXIN
/**
 * @description 检测微信小程序更新
 */
export function checkWxMpUpdate() {
  if (uni.getUpdateManager) {
    const updateManager = uni.getUpdateManager()
    updateManager.onCheckForUpdate(() => {})
    updateManager.onUpdateReady(() => {
      uni.showModal({
        title: "更新提示",
        content: "新版本已经准备好，是否重启应用？",
        success(res) {
          if (res.confirm) {
            updateManager.applyUpdate()
          }
        }
      })
    })
    updateManager.onUpdateFailed(() => {
      // 新的版本下载失败
      uni.showModal({
        title: "已经有新版本了哟~",
        content: "新版本已经上线啦~，请您删除当前小程序，重新搜索打开~",
        showCancel: false
      })
    })
  }
}
// #endif

/**
 * 小程序回退阻止提示
 * @param message 回退阻止时候的提示语
 */
export const enableAlertBeforeUnload = (message, callBack) => {
  //  #ifdef MP-WEIXIN
  wx.enableAlertBeforeUnload({
    message,
    success: () => {
      callBack && callBack()
    }
  })
  // #endif

  //  #ifdef MP-ALIPAY
  my.enableAlertBeforeUnload({
    message,
    success: () => {
      callBack && callBack()
    }
  })
  // #endif

  //  #ifdef MP-TOUTIAO
  tt.enableAlertBeforeUnload({
    message,
    success: () => {
      callBack && callBack()
    }
  })
  // #endif
}

export const disableAlertBeforeUnload = () => {
  //  #ifdef MP-WEIXIN
  wx.disableAlertBeforeUnload({})
  // #endif

  //  #ifdef MP-ALIPAY
  my.disableAlertBeforeUnload({})
  // #endif

  //  #ifdef MP-TOUTIAO
  tt.disableAlertBeforeUnload({})
  // #endif
}
